﻿function IContainer(){
    var _oInstances={};
    var _oContainerSupport={};
    var _oContextSupportInstances=[];
    var _oContainerContext=new ContainerContext();
	var _this=this;
	var _oContainerDefinition=null;
    
    _this.load=function(oContainerDefinition,callBack){
		_oContainerDefinition=oContainerDefinition;
        
        autoWire(oContainerDefinition);
		
		try{
			callBack(_oContainerContext);
		}catch(e){
			//	Callback failed
		}
    }
    
    /**
    *   @name   :   autoWire
    *   @desc   :   Takes a container definition as a parameter, creates class instances and wires them by reference
    */
    function autoWire(oContainerDefinition){
		
        //  Create instances in order of dependancy and wire constructor args
        forEach(oContainerDefinition,function(i,n){
            createInstance(i,n);
        });
        
        //  Set properties
        forEach(oContainerDefinition,function(i,n){

            var oNode=_oInstances[i];
            
            if(n.props!=null){
                forEach(n.props,function(i,n){
					var ref=n.ref;
					var val=n.val;
					
					//	Try using a get/set method like Java
                    var methodToInvoke='set'+getMethodName((n.name!=null)?n.name:n.ref);
					if(!oNode[methodToInvoke]){
						//	Use a standard javascript property
						methodToInvoke=getMethodName((n.name!=null)?n.name:n.ref);
					}
					
					var passIn=null;
					if(ref!=null){passIn=_oInstances[ref];}
					if(val!=null){passIn=val;}
					
                    oNode[methodToInvoke](passIn);
                });
            }
        });
        
    }
    
    /**
    *   @name   :   createInstance
    *   @para   :   [String]    i   An alphnumeric id used to hash the instance in the instance hash object
    *   @para   :   [Object]    n   The node definition instance
    */
    function createInstance(i,n){
		
		var argumentInstanaces=[];
        
		//	If the instance does not already exist make it
        if(_oInstances[i]==null){
			//alert("Creating an instance of "+i);
            if(n.args!=null){
				//	Iterate args
				function onEachArgument(x,y){
					
					if(y && y.ref){
						//alert("Adding a reference to "+y.ref+" to args list");
						try{
							argumentInstanaces.push(createInstance(y.ref,_oContainerDefinition[y.ref]));
						}catch(e){
							alert(e);
						}
					}else{
						argumentInstanaces.push(y);
					}
				}
				
				$.each(n.args,onEachArgument);
			}
			
            _oInstances[i]={};
			//alert(argumentInstanaces);
            n.type.apply(_oInstances[i],argumentInstanaces);
        }
        //alert("Returning instance of "+i);
		//	Return the instance
        return _oInstances[i];
    }
    
    
    /**
    *   @name   :   ContainerContext
    *   @desc   :   An inner class that encapsulates container context access
    */
    function ContainerContext(){
		var _this=this;
        //  Get a bean by id
        _this.getBean=function(sBeanId){
            return _oInstances[sBeanId];
        }
		
		_this.getBeans=function(){
            return _oInstances;
        }
    }
    
    
    function getMethodName(str){
        return String(str).replace(/\b\w/gi,function(c,i,s){
            return c.toUpperCase();
        });
    }
    
    function forEach(oCollection,fCallback){
        if(isArray(oCollection)){
            for(iIndex=0;iIndex<oCollection.length;iIndex++){
                callBack(iIndex,oCollection[iIndex]);
            }
        }else{
            for(each in oCollection){
                callBack(each,oCollection[each]);
            }
        }
        
        function callBack(i,n){
            try{
                fCallback(i,n);
            }catch(e){
            }
        }
    }
    
    function isArray(obj) {
       return (obj.constructor.toString().indexOf("Array") == -1)?false:true;
    }
}

